package game.jrogue;

/**
 * 
 * @author Mike
 * 
 * Code for management of beings in the Tyrant universe
 */
public class Being {

	protected static void setStats(Thing t, int[] bs) {
		for (int i = 0; i < bs.length; i++) {
			t.set(RPG.stats[i], bs[i]);
		}
	}
	
	public static boolean tryDrop(Thing h, Thing t) {
		if (t.place!=h) throw new Error("사물이 올바르지 못한 위치에 있습니다!");
		
		// break if worn and cursed
		if ((t.y > 0) && (t.getFlag("IsCursed"))) {
			h.message("당신은 "+Text.krSub(t.getYourName(), "을")+" 벗을 수 없습니다!");
			return false;
		}
		
		int total=t.getStat("갯수");
		if (h==Game.hero()&&(total > 1)) {
			int n=Game.getNumber("얼마나 많이 버리시겠습니까 (Enter=전부)? ",total);
			if (n>0) {
				t = t.remove(n);
			} else {
				Game.message("");
				return false;
			}
		}
		String dropName=t.getName(h);
		Item.drop(h,t);
		h.message(Text.krSub(dropName, "을") + " 버렸습니다.");
		h.incStat("APS", -Being.actionCost(h));
		return true;
	}

	public static boolean tryPickup(Thing h, Thing t) {
		boolean pickup = true;
		
		if (Item.isOwned(t)) {

			// find value
			int val=RPG.max(1,Item.value(t));
			
			// present options
			char c;
			if (t.getFlag("IsShopOwned")&&(!t.getFlag("IsMoney"))) {
				Thing sk=Item.findShopkeeper(t);
					
				// get shopkeeper price
				if (sk!=null) val = Item.shopPrice(t,h,sk);

				Game.message(t.getTheName() + " 의 가격은 "+ Coin.valueString(val)+ "입니다.");
				Game.message("구입(b), 훔침(s), 혹은 내버려두시겠습니까(l)? (b/s/l)");
				c = Game.getOption("sbl");
			} else {
				Game.message(Text.krSub(t.getTheName(), "은")+" 당신의 것이 아닙니다.");
				Game.message("훔치시겠습니까? (y/n)");
				c = Game.getOption("synl");
				if (c == 'y')
					c = 's';
			}
			Game.message("");

			if (c == 'l')
				pickup = false;
			if (c == 'b') {
				int funds = Coin.getMoney(h);
				if (val > funds) {
					Game.message("그것을 구입하기에 충분한 돈을 가지고 있지 않습니다!");
					Game.message("당신의 현재 소지금은 " + Coin.valueString(funds)+"입니다.");
					pickup = false;
				} else {
					Game.message("당신은 " + Coin.valueString(val)+"를 지불합니다.");
					Coin.removeMoney(h, val);
					Item.clearOwnership(t);
				}
			} else if (c == 's') {
				pickup = true;
			} else {
				pickup = false;
			}
		} 

		if (pickup) {
			h.incStat("APS", -Being.actionCost(h));
			Item.pickup(h,t);
		}
		
		return pickup;
	}
	
	public static int actionCost(Thing b) {
		return 200/(2+b.getStat(Skill.SLEIGHT));
		
	}
	
	/**
	 * Calculates the maximum carrying weight for a Being
	 * 
	 * @param b A Being
	 * @return Max weight in 100ths of stones
	 */
	public static int maxCarryingWeight(Thing b) {
		int st = (int)(b.getStat(RPG.ST_ST) * (1.0 + 0.25*b.getStat(Skill.TRADING)));

		int max= st * 10000+20000;
		max=max*b.getStat("CarryFactor")/100;
		return max;
	}

	public static boolean feelsFear(Thing b) {
		// TODO: account for insanity etc.
		return (b.getFlag("IsLiving")&&(!b.getFlag(Skill.BRAVERY)));
	}
	
	public static int calcInventoryWeight(Thing b) {
		Thing[] ts = b.getItems();

		int inventoryWeight = 0;
		for (int i = 0; i < ts.length; i++) {
			Thing t = ts[i];

			int wt = t.getWeight();
			inventoryWeight += wt;
        }
		return inventoryWeight;
	}
	
	public static void calcEncumberance(Thing b) {
		Thing[] ts = b.getItems();

		int enc = 0;
		int st = b.getStat(RPG.ST_ST);
		int inventoryWeight = 0;
		for (int i = 0; i < ts.length; i++) {
			Thing t = ts[i];

			int wt = t.getWeight();
			if ((t.y > 0) && (t.y != RPG.WT_MISSILE)) {
				// wielded items encumberance
				enc += wt / (st * 40);
			}
			inventoryWeight += wt;
        }

        // encumberance for % of max weight
		enc += (inventoryWeight * 100) / maxCarryingWeight(b);

		// encumberance if over-eaten
		// up to max of 50%
		int hungerMax=b.getStat(RPG.ST_HUNGERTHRESHOLD);
		int hl=0;
		if (hungerMax>0) {
			hl=(100*b.getStat(RPG.ST_HUNGER))/hungerMax;
		}
		if (hl<0) {
			enc+= (-hl)/2;
		}
		
		// no encumberance for first 15%
		// also trader bonus
		enc = RPG.middle(0, enc - (20 + 3 * b.getStat(Skill.TRADING)),100);

		b.set(RPG.ST_ENCUMBERANCE, enc);
	}

	public static double getHealth(Thing b) {
		double health= b.getStat("HPS")/(double)b.getStat("HPSMAX");
		if (health<=0.0) return 0.0;
		if (health>=1.0) return 1.0;
		return health;
	}
	
	public static void heal(Thing b, int h) {
		if (!b.getFlag("IsBeing"))
			return;

		// healing damages undead!
		if (b.getFlag("IsUndead")) h=-h;
		
		int hps = b.getStat("HPS");
		int hpsmax = b.getStat("HPSMAX");
		int gain = RPG.min(h, hpsmax - hps);
		if (gain > 0) {
			if (gain > hps) {
				b.message("당신의 몸이 훨씬 편해진것 같습니다!");
			} else if ((gain + hps) == hpsmax) {
				b.message("당신은 최고의 기분을 느낍니다");
			} else {
				b.message("몸이 좀 편해진 것 같습니다.");
			}
		} else if (hps == hpsmax) {
			b.message("당신은 펄펄 날아다닐것 같은 기분을 느낍니다!");
		} else if (gain < 0) {
			b.message("왠지 몸이 더 안좋아진 것 같습니다");
		}
		b.incStat("HPS", gain);
	}

	// gain multiple levels to a given target
	public static void gainLevel(Thing t, int targetlevel) {
		while (t.getStat(RPG.ST_LEVEL) < targetlevel)
			gainLevel(t);
	}
	
	public static void gainStat(Thing t, String stat) {
		gainStat(t,stat,1);
	}
	
	public static void gainStat(Thing t, String stat, int a) {
		t.incStat(stat,a);
		if (a==0) return;
		
		if (stat.equals("SK")) t.message("당신의 반사신경이 더 날카로와 진 것 같습니다.");
		if (stat.equals("ST")) t.message("당신의 근육이 더 우락부락해진 것 같습니다.");
		if (stat.equals("AG")) t.message("더 몸이 가볍고 날렵해진 것 같습니다.");
		if (stat.equals("TG")) t.message("더 몸이 건강해진 것 같습니다.");
		if (stat.equals("IN")) t.message("더 생각이 깊어진 것 같습니다.");
		if (stat.equals("WP")) t.message("당신은 자신이 좀 더 현명해진 것 같다고 느낍니다.");
		if (stat.equals("CH")) t.message("자기자신에 대해 더 많이 자신감이 붙은 것 같습니다.");
		if (stat.equals("CR")) t.message("이전엔 못 느꼈던 영감이 강해진 것을 느낍니다.");
	}
	
	public static int statIndex(String s) {
		return RPG.index(s,stats);
	}
	
	private static final String[] stats={"SK","ST","AG","TG","IN","WP","CH","CR"};
	public static int averageStat(Thing b) {
		int result=0;
		
		for (int i=0; i<stats.length; i++) {
			result+=b.getBaseStat(stats[i]);
		}
		return result/8;
	}

	// gain a level
	// enhance stats / skills as needed
	public static final double statLevelGain = 0.15;

	public static String[] statNames() {
		return stats;
	}
	
	public static void registerKill(Thing h, Thing t) {		
		if ((h!=null)&&t.getFlag("IsBeing")) {
			int killlevel = t.getLevel();
			if (h.isHero()) {
				Hero.gainKillExperience(h, t);
				
			}
			if (h.getStat(RPG.ST_SCORE_BESTKILL) < killlevel) {
				h.set(RPG.ST_SCORE_BESTKILL, killlevel);
			}
			h.incStat(RPG.ST_SCORE_KILLS, 1);
		}

		Event e=new Event("Kill");
		e.set("Killer",h);
		e.set("Target",t);
		Quest.notifyKill(e);
	}

	public static void gainLevel(Thing t) {
		int hpgain = t.getBaseStat("TG") / 3;
		int mpgain = t.getBaseStat("WP") / 3;
		t.multiplyStat("HPS", 1.0+hpgain/(double)t.getStat("HPSMAX"));
		t.multiplyStat("MPS", 1.0+mpgain/(double)t.getStat("MPSMAX"));
		t.incStat("HPSMAX", hpgain);
		t.incStat("MPSMAX", mpgain);
		
		int level = t.getStat(RPG.ST_LEVEL) + 1;
		t.set(RPG.ST_LEVEL, level);
		t.incStat(RPG.ST_SKILLPOINTS, 1);

		if (t.isHero()) {
			Game.message("당신은 " + level+ " 레벨이 되었습니다.");
		
			// guarantee at least one fate point
			if (t.getBaseStat(RPG.ST_FATE)<=0) {
				t.set(RPG.ST_FATE,1);
			}
		} else {
			// add skills automatically for creatures/NPCs
			autoLearnSkills(t);
		}
		
		for (int i = 0; i < 8; i++) {
			int stat = t.getBaseStat(RPG.stats[i]);
			double gain = statLevelGain;
			
			if ( (RPG.d(2)==1) ) {
				gainStat(t,RPG.stats[i], RPG.round(stat * gain));
			}
		}

	}

	// use initially gifted items
	// not very discerning
	public static void utiliseItems(Thing being) {
		if (!being.getFlag("IsIntelligent"))
			return;
		Thing[] stuff = being.getWieldableContents();
		if (stuff != null) {
			for (int i = 0; i < stuff.length; i++) {
				Thing it=stuff[i];
				
				// test before wielding
				// in case a previous wield has killed the creaure
				// this is unlikely, but possible
				if (it.place==being) {
					being.wield(it, it.getStat("장비종류"));
				}
			}
		}
	}

	public static int calcViewRange(Thing h) {
		int r = h.getStat("VisionRange");
		Map map=h.getMap();
		if (map!=null) {
			int mr=map.getStat("VisionRange");
			if ((mr>0)&&(mr<r)) r=mr;
		}
		return r;
	}

	// automatic skill gains for creatures/NPCs
	// TODO: make more focused based on NPC type
	private static final String[] commonSkills=new String[] {
		Skill.ATTACK,
		Skill.ATTACK,
		Skill.DEFENCE,
		Skill.DEFENCE,
		Skill.FEROCITY,
		Skill.ATHLETICS,
		Skill.BRAVERY,
		Skill.CASTING,
		Skill.FOCUS
	};
	public static void autoLearnSkills(Thing t) {
		while (t.getStat(RPG.ST_SKILLPOINTS) > 0) {
			t.incStat(RPG.ST_SKILLPOINTS,-1);
			String[] ss; 
			if (RPG.d(3)==1) {
				ss=Skill.fullList();
			} else {
				ss=commonSkills;
			}
			
			String sk=ss[RPG.r(ss.length)];
			t.incStat(sk,1);
			
			// create various acouterements to accompany new skills
			if (t.getFlag("IsIntelligent")&&t.getFlag("IsLiving")) {
				int level=t.getLevel();
				if (Spell.isOrder(sk)) {
					t.addThing(Spell.randomSpell(sk,t.getLevel()));
				} else if (sk.equals("Throwing")&&(RPG.d(3)==1)) {
					t.addThing(Lib.createType("IsThrowingWeapon",level));
				} else if (sk.equals("Archery")&&(RPG.d(2)==1)) {
					Thing rw=Lib.createType("IsRangedWeapon",level);
					t.addThing(rw);
					t.addThing(RangedWeapon.createAmmo(rw,level));
				} else if (sk.equals("Throwing")&&(RPG.d(2)==1)) {
					Thing tw=Lib.createType("IsThrowingWeapon",level);
					t.addThing(tw);
				} else if (sk.equals("Alchemy")&&(RPG.d(3)==1)) {
					t.addThing(Lib.createType("IsPotion",level));
				}
				
			}
			
		}
	}

	public static void recover(Thing t, int time) {

		int mps = t.getStat("MPS");
		if (mps<0) {
			t.incStat("MPS",-mps);
			Damage.inflict(t,mps,"특수피해");
			mps=0;
		}
		
		boolean hungry = false;
		if (t.isHero()) {
			int hunger = t.getStat(RPG.ST_HUNGER);
			int hungerThreshold = t.getStat(RPG.ST_HUNGERTHRESHOLD);			
			hungry = hunger>hungerThreshold;
		}
		
		// recharge hit points if not very hungry
		if (!hungry) {
			// regenerate hit points
			int regenerate = t.getStat(RPG.ST_REGENERATE);
			int heal=t.getStat(Skill.HEALING);
			
			int gain=0;
			int hpsmax = t.getStat(RPG.ST_HPSMAX);
			int hps = t.getStat("HPS");
			if (regenerate > 0) {
				int tg = t.getStat("TG");
				gain+= RPG.round(((float) time	* regenerate * tg) / 1000000);
			}
			if (heal>0) {
				gain+= RPG.round(((float) time	* heal * 50 * hpsmax) / 1000000);
			}
			t.set("HPS",RPG.middle(hps,hps+gain,hpsmax));

			int recharge = t.getStat(RPG.ST_RECHARGE);

			if (recharge > 0) {
				int mpsmax = t.getStat(RPG.ST_MPSMAX);
				t.set("MPS",RPG.middle(mps,mps+ RPG.round(((float) time* recharge * mpsmax) / 1000000),mpsmax));
			}
			
			if (t.getFlag(Skill.CASTING)) {
				Spell.rechargeSpells(t,time);
			}
		}

	}
	
	public static void init() {
	    Thing t;

        t = Lib.extend("기본 존재", "기본 사물");
        t.set("HPS",1);
        t.set("ImageSource", "Creatures");
        t.set("이미지", 340);
        t.set("IsBeing",1);
        t.set("IsMobile",1);
        t.set("IsLiving",1);
        t.set("IsActive",1);
        t.set("IsPhysical",1);
        t.set("IsJumpable",1);
        t.set("IsDestructible",1);
        t.set("IsDisplaceable",1);
        t.set("IsRegenerating",1);
        t.set("CarryFactor",100);
        t.set("VisionRange",6);
		t.set(RPG.ST_RECHARGE, 200);
		t.set(RPG.ST_REGENERATE, 40);
		t.set("속도",100);
        t.set("이동속도",100);
        t.set("공격속도",100);
        t.set("빈도",50);
        t.set("도덕성향","N");
        t.set("IsBlocking", 1);
        t.set("이동비용", 100);
        t.addHandler("OnAction",new AI.AIScript());
        t.set("시체그래픽", "blood pool");
        t.set("RES:water",35);
        t.set("NameType", Description.NAMETYPE_NORMAL);
        t.set("Z", Thing.Z_MOBILE);
        Lib.add(t);
	}
}